/*
 * jopengl_GLU.cpp
 *
 *  Created on: 2013-10-24
 *      Author: pan.jinghua
 */


#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include "jopengl_GLU.h"
#include "util.h"

#define giv(jia, iv) GLint* iv = (GLint*)env->GetIntArrayElements(jia, 0)
#define riv(jia, iv) env->ReleaseIntArrayElements(jia, (jint*)iv, 0)

#define gdv(jda, dv) GLdouble* dv = (GLdouble*)env->GetDoubleArrayElements(jda, 0)
#define rdv(jda, dv) env->ReleaseDoubleArrayElements(jda, (jdouble*)dv, 0)

jint JNICALL Java_jopengl_GLU_gluBuild1DMipmaps
  (JNIEnv *env, jclass clz, jint target, jint internalFormat, jint width, jint format, jintArray data) {
    giv(data, iv);
    jint r = gluBuild1DMipmaps(target, internalFormat, width, format, GL_INT, iv);
    riv(data, iv);
    return r;
}

jint JNICALL Java_jopengl_GLU_gluBuild2DMipmaps
  (JNIEnv *env, jclass clz, jint target, jint internalFormat, jint width, jint height, jint format, jintArray data) {
    giv(data, iv);
    jint r = gluBuild2DMipmaps(target, internalFormat, width, height, format, GL_INT, iv);
    riv(data, iv);
    return r;
}

void JNICALL Java_jopengl_GLU_gluLookAt
  (JNIEnv *env, jclass clz, jdouble eyeX, jdouble eyeY, jdouble eyeZ, jdouble centerX, jdouble centerY, jdouble centerZ, jdouble upX, jdouble upY, jdouble upZ) {
    gluLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
}

void JNICALL Java_jopengl_GLU_gluOrtho2D
  (JNIEnv *env, jclass clz, jdouble left, jdouble right, jdouble bottom, jdouble top) {
    gluOrtho2D(left, right, bottom, top);
}

void JNICALL Java_jopengl_GLU_gluPerspective
  (JNIEnv *env, jclass clz, jdouble fovy, jdouble aspect, jdouble zNear, jdouble zFar) {
    gluPerspective(fovy, aspect, zNear, zFar);
}

jint JNICALL Java_jopengl_GLU_gluScaleImage
  (JNIEnv *env, jclass clz, jint format, jint wIn, jint hIn, jintArray dataIn, jint wOut, jint hOut, jintArray dataOut) {
    giv(dataIn, ivIn);
    giv(dataOut, ivOut);
    jint r = gluScaleImage(format, wIn, hIn, GL_INT, ivIn, wOut, hOut, GL_INT, ivOut);
    riv(dataIn, ivIn);
    riv(dataOut, ivOut);
    return r;
}

void pointSphere(float ry, float rxz, float axy, float axz, float kxz, float ky) {
    float pr = ry * rxz / sqrtf(ry * ry * cosf(axy) * cosf(axy) + rxz * rxz * sinf(axy) * sinf(axy));
    float prxz = pr * cosf(axy);
    float px = prxz * cosf(axz);
    float py = pr * sinf(axy);
    float pz = prxz * sinf(axz);

    float naxy;
    if ((rxz - fabs(prxz)) / rxz < 1e-6) naxy = prxz > 0 ? 0 : M_PI;
    else naxy = atanf(-ry * prxz / rxz / sqrtf(rxz * rxz - prxz * prxz)) + M_PI / 2;
    if (py < 0) naxy = -naxy;
    float nx = cosf(naxy) * cosf(axz);
    float ny = sinf(naxy);
    float nz = cosf(naxy) * sinf(axz);

//    glNormal3f(px / pr, py / pr, pz / pr);
    glNormal3f(nx, ny, nz);
    glTexCoord2f(kxz, ky);
    glVertex3f(px, py, pz);
}

void JNICALL Java_jopengl_GLU_gluSolidSphere(JNIEnv *env, jclass clz,
        jfloat radiusXZ, jfloat radiusY, jint sliceXZ, jint sxz1, jint sxz2, jint sliceY, jint sy1, jint sy2) {
    glBegin(GL_TRIANGLES);
    for (int sy = sy1; sy < sy2; sy++) {
        float ky1 = 1.0 * sy / sliceY;
        float ky2 = 1.0 * (sy + 1) / sliceY;
        float axy1 = M_PI * (0.5 - ky1);
        float axy2 = M_PI * (0.5 - ky2);
        for (int sxz = sxz1; sxz < sxz2; sxz++) {
            float kxz1 = 1.0 * sxz / sliceXZ;
            float kxz2 = 1.0 * (sxz + 1) / sliceXZ;
            float axz1 = 2 * M_PI * kxz1;
            float axz2 = 2 * M_PI * kxz2;

            pointSphere(radiusY, radiusXZ, axy1, axz2, kxz2, ky1);
            pointSphere(radiusY, radiusXZ, axy2, axz2, kxz2, ky2);
            pointSphere(radiusY, radiusXZ, axy2, axz1, kxz1, ky2);

            pointSphere(radiusY, radiusXZ, axy1, axz2, kxz2, ky1);
            pointSphere(radiusY, radiusXZ, axy2, axz1, kxz1, ky2);
            pointSphere(radiusY, radiusXZ, axy1, axz1, kxz1, ky1);
        }
    }
    glEnd();
}

jlong JNICALL Java_jopengl_GLU_gluCreateFont(JNIEnv *env, jclass clz, jboolean ansi, jstring fontName, jint height,
        jint weight, jboolean italic, jint angle, jboolean underline, jboolean strikeOut) {
    const char* cUtf8 = env->GetStringUTFChars(fontName, 0);
    const char* cGBK = conv(cUtf8, CP_UTF8, CP_ACP);
    printf("font: %s\n", cGBK);
    UINT charset = ansi ? ANSI_CHARSET : GB2312_CHARSET;
    HFONT font = CreateFontA(height, 0, angle, angle, weight, italic, underline, strikeOut, charset,
            OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, cGBK);
    env->ReleaseStringUTFChars(fontName, cUtf8);
    delete[] cGBK;
    return (jlong)font;
}

void JNICALL Java_jopengl_GLU_gluReleaseFont(JNIEnv *env, jclass clz, jlong fontAddr) {
    DeleteObject((HGDIOBJ)fontAddr);
}

jint JNICALL Java_jopengl_GLU_gluLoadAnsiFontIndex(JNIEnv *env, jclass clz, jlong fontAddr) {
    HDC hdc = wglGetCurrentDC();
    HFONT oldFont = (HFONT)SelectObject(hdc, (HFONT)fontAddr);
    GLuint list = glGenLists(128);
    bool r = wglUseFontBitmapsA(hdc, 0, 128, list);
    printf("wglUseFontBitmapsA %d\n", r);
    SelectObject(hdc, oldFont);
    return list;
}

jint JNICALL Java_jopengl_GLU_gluLoadGBKFontIndex(JNIEnv *env, jclass clz, jlong fontAddr, jstring chars) {
    HDC hdc = wglGetCurrentDC();
    HFONT oldFont = (HFONT)SelectObject(hdc, (HFONT)fontAddr);
    const char* cUtf8 = env->GetStringUTFChars(chars, 0);
    const char* cGBK = conv(cUtf8, CP_UTF8, CP_ACP);
    int len = MultiByteToWideChar(CP_UTF8, 0, cUtf8, -1, NULL, 0);
    GLuint list = glGenLists(len);
    int i = list;
    for (const char* c = cGBK; *c; c++) {
        if (IsDBCSLeadByte(*c)) {
            wglUseFontBitmapsW(hdc, *(wchar_t*)c, 1, i++);
            c++;
        } else {
            wglUseFontBitmapsA(hdc, *c, 1, i++);
        }
    }
    env->ReleaseStringUTFChars(chars, cUtf8);
    delete[] cGBK;
    SelectObject(hdc, oldFont);
    return list;
}

void JNICALL Java_jopengl_GLU_gluTextAnsi(JNIEnv *env, jclass clz, jint list, jstring str) {
    const char* cUtf8 = env->GetStringUTFChars(str, 0);
    for (const char* c = cUtf8; *c; c++) {
        glCallList(list + *c);
    }
    env->ReleaseStringUTFChars(str, cUtf8);
}

void JNICALL Java_jopengl_GLU_gluTextIndex(JNIEnv *env, jclass clz, jintArray index) {
    int len = env->GetArrayLength(index);
    giv(index, iv);
    for (int i = 0; i < len; i++) {
        glCallList(iv[i]);
    }
    riv(index, iv);
}

jdoubleArray JNICALL Java_jopengl_GLU_gluUnProject__DDD_3D_3D_3I_3D
  (JNIEnv *env, jclass clz, jdouble winX, jdouble winY, jdouble winZ, jdoubleArray model, jdoubleArray proj, jintArray view, jdoubleArray objXYZ) {
    gdv(model, mv);
    gdv(proj, pv);
    giv(view, vv);
    gdv(objXYZ, ov);
    jdoubleArray result = objXYZ;
    if (!gluUnProject(winX, winY, winZ, mv, pv, vv, &ov[0], &ov[1], &ov[2])) result = NULL;
    rdv(model, mv);
    rdv(proj, pv);
    riv(view, vv);
    rdv(objXYZ, ov);
    return result;
}
